<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<title>OrbitControls控制器案例</title>
		<style type="text/css">
			html,
			body {
				margin: 0;
				height: 100%;
			}

			canvas {
				display: block;
			}
			
			/* 进度条 */
			#progress {
				position: fixed;
				width: 100%;
				height: 100%;
				border: 1px solid blue;
				/*background: url("./img/loading.gif") no-repeat;*/
                background-color:rgba(0,0,0,255,.5);
				background-size: 100% 100%;
				z-index: 99;
			
			}
			
			.insetBox {
				width: 80%;
				height: 50px;
				/* background-color: blue; */
				position: absolute;
				left: 50%;
				top: 60%;
				transform: translate(-50%, -50%);
				display: flex;
				flex-direction: column;
				align-items: center;
			}
			
			#progress .mask {
				/* width: 80%; */
				height: 30px;
				line-height: 30px;
				color: #FFF;
				text-shadow: .1px .1px 1px rgba(0, 0, 0, .5);
				/* background: rgba(0, 0, 0, 0.2); */
			}
			
			#progress .loading {
				width: 100%;
				height: 10px;
				background: #fff;
				z-index: 100;
				border-radius: 1em;
			}
			
			#progress .trends {
				width: 0;
				height: 100%;
				background:  #0F0;
				box-shadow: 1px 1px 10px #0F0;
				border-radius: 1em;
			}
		</style>

	</head>
	<body>
		<div id="container">
			<div id="progress">
				<div class="insetBox">
					<div class="mask"></div>
					<div class="loading">
						<div class="trends"></div>
					</div>
				</div>
			
			</div>
		</div>
	</body>
	<script src="./js/jquery-3.2.1.min.js"></script>
	<script src="./js/three.min.js"></script>
	<script src="./js/GLTFLoader.js"></script>
	<script src="./js/OrbitControls.js"></script>
	<script src="./js/DRACOLoader.js"></script>
	<script src="./js/Tween.js"></script>
	<script src="./js/stats.min.js"></script>
	<script>
		console.log(THREE.REVISION)
		var renderer, camera, scene, gui, stats, ambientLight, directionalLight, controls;

		const container = document.getElementById('container');

		// var plane
		/**
		 * 判断是否是手机端
		 * @returns {boolean}
		 */
		const isMobile = () => navigator.userAgent.toLowerCase().match(
			/(ipod|ipad|iphone|android|coolpad|mmp|smartphone|midp|wap|xoom|symbian|j2me|blackberry|wince)/i) != null
            window.onload = function(){
      draw();

            }
		function draw() {
			initRender();
			initScene();
			initCamera();
			addLight()
			// floor();
			initStats();

			initControl();
			loadModuleByDRACOLoader('./dracoModels/', "car_d_showRoom.glb", false, 50, 0);
			animate();
			window.onresize = onWindowResize;

			const plane = new THREE.Mesh(new THREE.PlaneGeometry(1000, 1000), new THREE.MeshStandardMaterial({
				color: '#fff',
				side: THREE.DoubleSide
			}))
			plane.rotateX(-Math.PI / 2)
			plane.name = 'plane'
			plane.position.set(0, 0.1, 0)
			scene.add(plane)
			plane.receiveShadow = true

			const circle = new THREE.Mesh(new THREE.CircleGeometry(5, 50), new THREE.MeshBasicMaterial({
				color: 'green',
				transparent: true,
				opacity: 0.2,
				side: THREE.DoubleSide
			}))
			circle.visible = false
			circle.rotateX(-0.5 * Math.PI)
			circle.name = 'circle'
			scene.add(circle)
			let circleBorder
			const loader = new THREE.TextureLoader()
			// loader.load("../public/images/ysThree/redCircle2.png",texture =>{
			loader.load("./img/2.png", texture => {
				circle.material = new THREE.MeshBasicMaterial({
					map: texture,
					transparent: true,
					opacity: 0.9,
					side: THREE.DoubleSide,
					depthTest: false
				})
				circleBorder = new THREE.Mesh(new THREE.CircleGeometry(5, 50), circle.material)
				circleBorder.position.y = 0.2
				circle.add(circleBorder)
			})


			let timeout; //用于存储定时器的变量
			let isMoving = false
			$("#container").mousedown(function() {
				timeout = setTimeout(function() {
					isMoving = true
					circle.visible = false
					$("body").css({
						cursor: 'grab'
					})
				}, 200); //鼠标按下0.5秒后发生alert事件
			}).mouseup(function() {
				isMoving = false
				clearTimeout(timeout); //清理掉定时器
			})
			let currentHover
			container.addEventListener('mousemove', function(event) {
				event.preventDefault()
				const res = getIntersectObject(container, event)
				if (res.objectList.length > 0) {
					const obj = res.objectList[0]
					// console.log(obj.object)
					if ((obj.object.name === 'plane' || obj.object.name === 'circle') && !isMoving) { //如果是面或当前circle
						circle.position.copy(obj.point)
						circle.position.y = 0.2
						circle.visible = true
						$("body").css({
							cursor: 'pointer'
						})

						if (currentHover)
							currentHover.visible = false
						currentHover = null
					} else {
						circle.visible = false
						if (obj.object.name === 'box') {
							if (currentHover) {
								currentHover.visible = false
							}
							currentHover = obj.object.getObjectByName('border')
							currentHover.visible = true
						} else {
							if (currentHover)
								currentHover.visible = false
							currentHover = null
						}
					}
				} else {
					circle.visible = false
					$("body").css({
						cursor: 'grab'
					})
					if (currentHover)
						currentHover.visible = false
					currentHover = null
				}
			})
			let haveClick = false
			container.addEventListener('click', function(event) {
				event.preventDefault()
				const res = getIntersectObject(container, event)
				if (res.objectList.length > 0) {
					const obj = res.objectList[0]
					if (obj.object.name === 'circle') {
						const x = circle.position.x,
							z = circle.position.z,
							y = camera.position.y,
							v = camera.position.clone().sub(controls.target.clone()) //差
						haveClick = true
						flyTo(TWEEN, controls, {
							position: [x, y, z],
							controls: [x - v.x, y - v.y, z - v.z],
							duration: 500,
							done: function() {

							}
						})
					} else if (obj.object.name === 'box') {

					}
				} else {

				}
			})

			/**
			 * 获取与射线相交的对象数组
			 * @param el
			 * @param event
			 * @param parent
			 * @param recursive
			 */
			const mouse = new THREE.Vector2()
			const raycaster = new THREE.Raycaster()

			function getIntersectObject(el, event, parent, recursive) {
				event.preventDefault()
				let objectList = []
				try {
					mouse.x = (((event.clientX || (event.touches[0] ? event.touches[0].pageX : event.changedTouches[0]
						.pageX)) - el.getBoundingClientRect().left) / el.offsetWidth) * 2 - 1
					mouse.y = -(((event.clientY || (event.touches[0] ? event.touches[0].pageY : event.changedTouches[0]
						.pageY)) - el.getBoundingClientRect().top) / el.offsetHeight) * 2 + 1
					raycaster.setFromCamera(mouse, camera)
					// intersectObjects(object,recursive)object — 用来检测和射线相交的物体。recursive — 如果为true，它还检查所有后代。否则只检查该对象本身。缺省值为false。
					objectList = raycaster.intersectObjects((parent || scene).children, recursive)
				} catch (e) {
					// 鼠标越界
				}
				return {
					raycaster: raycaster,
					objectList: objectList
				}
			}


			//手机端 手机端监听点击使用 touchstart
			if (isMobile()) {
				container.addEventListener('touchstart', function(event) {
					event.preventDefault()
					const res = getIntersectObject(container, event)
					if (res.objectList.length > 0) {
						const obj = res.objectList[0]
						if (obj.object.name === 'plane') {
							circle.position.copy(obj.point)
							circle.position.y = 0.01
							const x = circle.position.x,
								z = circle.position.z,
								y = camera.position.y,
								v = camera.position.clone().sub(controls.target.clone()) //差
							haveClick = true
							flyTo(TWEEN, controls, {
								position: [x, y, z],
								controls: [x - v.x, y - v.y, z - v.z],
								duration: 500,
								done: function() {

								}
							})
						} else if (obj.object.name === 'box') {

						}
					} else {

					}
				})
			}

			/**
			 * 相机飞行
			 * @param TWEEN
			 * @param controls
			 * @param option
			 * @returns {string|*}
			 */
			function flyTo(TWEEN, controls, option) {
				option.position = option.position || [] // 相机新的位置
				option.controls = option.controls || [] // 控制器新的中心点位置(围绕此点旋转等)
				option.duration = option.duration || 1000 // 飞行时间
				option.easing = option.easing || TWEEN.Easing.Linear.None
				TWEEN.removeAll()
				const curPosition = camera.position
				const controlsTar = controls.target
				const tween = new TWEEN.Tween({
					x1: curPosition.x, // 相机当前位置x
					y1: curPosition.y, // 相机当前位置y
					z1: curPosition.z, // 相机当前位置z
					x2: controlsTar.x, // 控制当前的中心点x
					y2: controlsTar.y, // 控制当前的中心点y
					z2: controlsTar.z // 控制当前的中心点z
				}).to({
					x1: option.position[0], // 新的相机位置x
					y1: option.position[1], // 新的相机位置y
					z1: option.position[2], // 新的相机位置z
					x2: option.controls[0], // 新的控制中心点位置x
					y2: option.controls[1], // 新的控制中心点位置x
					z2: option.controls[2] // 新的控制中心点位置x
				}, option.duration).easing(TWEEN.Easing.Linear.None) // TWEEN.Easing.Cubic.InOut //匀速
				tween.onUpdate(() => {
					controls.enabled = false
					camera.position.set(tween._object.x1, tween._object.y1, tween._object.z1)
					controls.target.set(tween._object.x2, tween._object.y2, tween._object.z2)
					controls.update()
					if (option.update instanceof Function) {
						option.update(tween)
					}
				})
				tween.onStart(() => {
					controls.enabled = false
					if (option.start instanceof Function) {
						option.start()
					}
				})
				tween.onComplete(() => {
					controls.enabled = true
					if (option.done instanceof Function) {
						option.done()
					}
				})
				tween.onStop(() => option.stop instanceof Function ? option.stop() : '')
				tween.start()
				TWEEN.add(tween)
				return tween
			}

		}

		function initRender() {
			renderer = new THREE.WebGLRenderer({
				antialias: true
			});
			renderer.setSize(window.innerWidth, window.innerHeight);
			//告诉渲染器需要阴影效果
			renderer.shadowMap.enabled = true;
			renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 默认的是，没有设置的这个清晰 THREE.PCFShadowMap
			container.appendChild(renderer.domElement);
		}

		function initCamera() {
			camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000);
			camera.position.set(0, 100, 200);
			camera.lookAt(new THREE.Vector3(0, 0, 0));


		}

		function initScene() {
			scene = new THREE.Scene();
			scene.background = new THREE.Color(0xa0a0a0);
		}


		//灯光
		function addLight() {
			scene.add(new THREE.AmbientLight(0x444444))

			const light = new THREE.DirectionalLight(0xffffff)
			light.position.set(0, 20, 20)

			light.castShadow = true
			light.shadow.camera.top = 10
			light.shadow.camera.bottom = -10
			light.shadow.camera.left = -10
			light.shadow.camera.right = 10

			//告诉平行光需要开启阴影投射
			light.castShadow = true
			scene.add(light)
		}

		function floor() {

			// 创建平面，几何体，材质
			const texture = new THREE.TextureLoader().load('./img/clicle.png'); // 纹理加载器
			texture.wrapS = THREE.RepeatWrapping;
			texture.wrapT = THREE.RepeatWrapping;
			texture.repeat.set(10, 10);
			var geometry = new THREE.PlaneGeometry(300, 300);; // 平面几何体
			var material = new THREE.MeshPhongMaterial({
				map: texture,
				side: THREE.DoubleSide
			});
			plane = new THREE.Mesh(geometry, material); // 创建模型
			plane.name = 'plane'
			plane.rotateX(Math.PI / 180 * 90)
			scene.add(plane) // 加入场景

			// const plane = new THREE.Mesh(new THREE.PlaneGeometry(1000,1000),new THREE.MeshStandardMaterial({color:'#fff',sde:THREE.DoubleSide}))
			// plane.rotateX(-Math.PI/2)
			// plane.name = 'plane'
			// plane.position.set(0,0.1,0)
			// scene.add(plane)
			// plane.receiveShadow = true

		}


		function initStats() {
			stats = new Stats();
			document.body.appendChild(stats.dom);
		}

		function initControl() {

			 controls = new THREE.OrbitControls(camera, renderer.domElement);
            // 如果使用animate方法时，将此函数删除
            //controls.addEventListener( 'change', render );
            // 使动画循环使用时阻尼或自转 意思是否有惯性
            controls.enableDamping = true;
            //动态阻尼系数 就是鼠标拖拽旋转灵敏度
            //controls.dampingFactor = 0.25;
            //是否可以缩放
            controls.enableZoom = false;
            //是否自动旋转
           // controls.autoRotate = true;
            controls.autoRotateSpeed = 0.3;
            //设置相机距离原点的最远距离
            controls.minDistance = 1;
            //设置相机距离原点的最远距离
            controls.maxDistance = 1000;
            //是否开启右键拖拽
            controls.enablePan = false
            // return controls
		}

		function render() {

			controls.update();

			renderer.render(scene, camera);
		}

		function onWindowResize() {

			camera.aspect = window.innerWidth / window.innerHeight;
			camera.updateProjectionMatrix();
			renderer.setSize(window.innerWidth, window.innerHeight);

		}

		function animate() {
			//更新控制器
			render();
			TWEEN.update()
			//更新性能插件
			stats.update();

			requestAnimationFrame(animate);
		}


		function loadModuleByDRACOLoader(path, modelName, setCenter, scale, position, rotation) {
			let scaleVec3, positionVec3;
			if (typeof scale == "number") {
				scaleVec3 = new THREE.Vector3(scale, scale, scale);
			} else {
				scaleVec3 = new THREE.Vector3(scale.x, scale.y, scale.z);
			}
			if (typeof position == "number") {
				positionVec3 = new THREE.Vector3(position, position, position);
			} else {
				positionVec3 = new THREE.Vector3(position.x, position.y, position.z);
			}
			let dracoLoader = new THREE.DRACOLoader();
			dracoLoader.setDecoderPath("./libs/draco/"); // 设置public下的解码路径，注意最后面的/
			dracoLoader.setDecoderConfig({
				type: "js"
			}); //使用兼容性强的draco_decoder.js解码器
			dracoLoader.preload();

			const loader = new THREE.GLTFLoader().setPath(path);
			loader.setDRACOLoader(dracoLoader);
			return new Promise((res, rj) => {
				loader.load(modelName, (gltf) => {
					if (setCenter) {
						gltf.scene.traverse(function(child) {
							if (setCenter && child.isMesh) {
								child.geometry.center();

							}
							if (object.type === "Mesh") {
								child.material = new THREE.MeshPhongMaterial({
									map: child.material.map,
									color: child.material.color,
									castShadow: true,
									receiveShadow: true
								});
							}
						});
					}
					// plane = gltf.scene.getObjectByName("地面")
					console.log(gltf.scene)
					gltf.scene.scale.copy(scaleVec3);
					gltf.scene.position.copy(positionVec3);
					if (rotation) {
						gltf.scene.rotation.copy(rotation);
					}
					scene.add(gltf.scene);
					res(gltf.scene);

					gltf = null;
				}, onProgress, onError);
			});
		}
		
		//进度通知
		var onProgress = function(xhr) {
			if (xhr.lengthComputable) {
		
				var percentComplete = xhr.loaded / xhr.total * 100;
				console.log(Math.round(percentComplete, 2) + '% downloaded');
				play(Math.round(percentComplete, 2))
		
			}
		
		};
		//报错通知
		var onError = function(xhr) {
			console.log("加载GLTF" + xhr)
		};
		
		function play(a) {
			var timeNum = 0;
			
		var timer = setInterval(()=>{
				timeNum++;
				
				document.getElementsByClassName("trends")[0].style.width = timeNum + "%";
				document.querySelector(".mask").innerHTML = timeNum + "%";
                console.log(timeNum)
				
				
				if(timeNum>100){
					clearInterval(timer)
                console.log(timeNum)
                    
					document.getElementById("progress").style.display = "none";
				}
				
			},100)
			
			/* document.getElementsByClassName("trends")[0].style.width = a + "%";
			document.querySelector(".mask").innerHTML = a + "%"; */
			if (a < 100) {
				document.getElementById("progress").style.display = "block";
			} else {
				setTimeout(() => {
					// document.getElementById("progress").style.display = "none";
				}, 10000)
		
			}
		}
	</script>
</html>